﻿using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using MyCraft.Minecraft_2D.Blocs;
using Serializer;
using System.Diagnostics;

namespace MyCraft.Minecraft_2D
{
    class MineCraft2D
    {
        #region Attributs
        int _width, _height, _blocSize;
        GamePadState _previousPad;
        KeyboardState _previousKeyboard;
        MouseState _previousMouse;
        float _top, _left, _speed;
        Map _map;
        Point _mouse, _pointeurPoint, _selectedBloc;
        Dictionary<string, Keys> _keys;
        Dictionary<string, Buttons> _xbox;
        Texture2D _pointeur, _pixel;
        SelectBloc _selectBloc;
        //List<List<Keys>> _keys;
        #endregion

        #region Constructeur
        public MineCraft2D(int width, int height)
        {
            _blocSize = 16;
            _map = new Map(300, 300);
            _width = width / _blocSize;
            _height = height / _blocSize;
            _left = Map.Length / 2;
            _top = MyCraft.Minecraft_2D.Map.Up((int)_left) - _height / 2;
            _left -= _width / 2;
            _speed = 0.5f;
            _mouse = new Point();
            _pointeurPoint = new Point(GraphicsDeviceManager.DefaultBackBufferWidth / 2, GraphicsDeviceManager.DefaultBackBufferHeight / 2);
            _selectedBloc = new Point();
            _selectBloc = new SelectBloc(new Vector2(5));
        }
        #endregion

        #region Propriétés

        #endregion

        #region Methodes

        private void Speed(bool speed, int elapsedTime)
        {
            if (speed)
                _speed = elapsedTime / 30f * 1.0f;
            else
                _speed = elapsedTime / 30f * 0.5f;
            //Alert.Alerte("Speed", "Speed = " + _speed, 1000, Color.Blue, null);
        }

        private void Save(GraphicsDevice device)
        {
            //SettingsManager.Save<Map>(_map);
            Map.Save(device);
            Alert.Alerte("Map Sauvegardée", Color.Blue);
        }

        private void Load(GraphicsDevice device)
        {
            //_map = SettingsManager.Load<Map>();
            Map.Load(device);

            _left = Map.Length / 2;
            _top = MyCraft.Minecraft_2D.Map.Up((int)_left) - _height / 2;
            _left -= _width / 2;

            Alert.Alerte("Map Chargée", Color.Blue);
        }

        private void FullScreen(GraphicsDeviceManager device)
        {
            bool full = device.IsFullScreen;
            //device.ToggleFullScreen();
            if (full)
            {
                device.IsFullScreen = false;
                device.PreferredBackBufferWidth = 800;
                device.PreferredBackBufferHeight = (int)Math.Ceiling(800 / device.GraphicsDevice.DisplayMode.AspectRatio);
                //this._blocSize = 16;
            }
            else
            {
                device.IsFullScreen = true;
                device.PreferredBackBufferWidth = device.GraphicsDevice.DisplayMode.Width;
                device.PreferredBackBufferHeight = device.GraphicsDevice.DisplayMode.Height;
                //this._blocSize = 16;
            }
            device.ApplyChanges();

            _width = device.PreferredBackBufferWidth / _blocSize - 2;
            _height = device.PreferredBackBufferHeight / _blocSize - 3;
            Alert.Alerte("Screen", "FullScreen, {W:" + device.PreferredBackBufferWidth + ", H:" + device.PreferredBackBufferHeight + "}", 4000, Color.Blue, null);
        }

        private void PlaceBlock(double timer)
        {
            Block b = _map[_selectedBloc];
            if (b.Type == TypeBloc.Air && b.Light > 1)
                foreach (Block bloc in b.Around())
                    if (bloc.Type != TypeBloc.Air)
                    {
                        b.Bloc = BlocSet.GetSingleton()._newBlocs[(TypeBloc)_selectBloc.SelectedBloc.Id].Invoke(new Vector2(
                                (float)(_pointeurPoint.X + (_left - _selectedBloc.X) * _blocSize) / _blocSize,
                                (float)(_pointeurPoint.Y + (_top - _selectedBloc.Y) * _blocSize) / _blocSize),
                            timer);
                        b.IsSun = false;
                        if (b.Bottom != null)
                            b.Bottom.Refresh();
                        return;
                    }
        }

        private void PickBlock()
        {
            Block b=_map[_selectedBloc];
            if (b.Type != TypeBloc.Air && b.Light > 0)
            {
                b.Bloc.Life -= _selectBloc.SelectedBloc.Hit;
                b.Refresh();
            }
        }
        #endregion

        #region XNA
        public void LoadContent(ContentManager content, out Game1.HasXbox updateDelegate)
        {
            _previousKeyboard = Keyboard.GetState();
            _previousMouse = Mouse.GetState();
            _pointeur = content.Load<Texture2D>("Pointeur");
            _pixel = content.Load<Texture2D>("Pixel");
            updateDelegate = UpdateKeyboard;
            SettingsManager.Save<Map>(_map, "_map");
            _selectBloc.LoadContent(content);

            //_keys = SettingsManager.Load<Dictionary<string, Keys>>("Keys");
            if (_keys == null || _keys.Count != 8)
            {
                Alert.Alerte("Clavier non assigné", Color.Red);
                _keys = new Dictionary<string, Keys>(8);
                _keys.Add("Up", Keys.Z);
                _keys.Add("Down", Keys.S);
                _keys.Add("Left", Keys.Q);
                _keys.Add("Right", Keys.D);
                _keys.Add("Speed", Keys.LeftShift);
                _keys.Add("Save", Keys.Escape);
                _keys.Add("Load", Keys.Enter);
                _keys.Add("FullScreen", Keys.F10);

                SettingsManager.Save<Dictionary<string, Keys>>(_keys, "Keys");
                Alert.Alerte("Clavier Sauvegardé", Color.Blue);
            }

            if (GamePad.GetState(PlayerIndex.One).IsConnected)
            {
                _previousPad = GamePad.GetState(PlayerIndex.One);
                _xbox = SettingsManager.Load<Dictionary<string, Buttons>>("Xbox");
                Alert.Alerte("Joueur 1 connécté");
                updateDelegate = UpdateController;
                if (_xbox == null || _xbox.Count != 8)
                {
                    Alert.Alerte("GamePad non assigné", Color.Red);
                    _xbox = new Dictionary<string, Buttons>(8);
                    _xbox.Add("Up", Buttons.DPadUp);

                    _xbox.Add("Down", Buttons.DPadDown);

                    _xbox.Add("Left", Buttons.DPadLeft);

                    _xbox.Add("Right", Buttons.DPadRight);

                    _xbox.Add("Speed", Buttons.A);

                    _xbox.Add("Save", Buttons.Back);

                    _xbox.Add("Load", Buttons.Start);

                    _xbox.Add("FullScreen", Buttons.BigButton);

                    SettingsManager.Save<Dictionary<string, Buttons>>(_xbox, "Xbox");
                    Alert.Alerte("GamePad Sauvegardé");
                }
            }

            BlocSet.GetSingleton().LoadContent(content);
        }

        public void Update(GameTime gameTime, GraphicsDeviceManager device)
        {
            int xd = (int)_left;
            int yd = (int)_top;
            int startX = Math.Max(0, xd);
            int endX = Math.Min(_width + xd + 4, Map.Length);
            int startY = Math.Max(0, yd);
            int endY = Math.Min(_height + yd + 4, Map.Height);

            for (int x = startX; x < endX; x++)
            {
                for (int y = startY; y < endY; y++)
                {
                    Block block = _map[new Point(x, y)];
                    block.Update(gameTime.TotalGameTime.TotalMilliseconds);
                }
            }

            if (_selectedBloc.X >= 0 && _selectedBloc.Y >= 0 && _selectedBloc.X < Map.Length && _selectedBloc.Y < Map.Height)
            {
                Block b = _map[_selectedBloc];
                Alert.Alerte("Mouse",
                    "{X:" + _selectedBloc.X + ", Y:" + _selectedBloc.Y + "}, L:" + b.Light + ", S:" + b.IsSun + ", T:" + b.Type + ", L:" + b.Bloc.Life + @"/" + b.Bloc.MaxLife,
                    4000, Color.Blue, null);
            }
            else
                Alert.Alerte("Mouse", "Mouse out", 4000, Color.Blue, null);
        }

        public void UpdateKeyboard(MouseState mouse, KeyboardState keyboard, GameTime gameTime, GraphicsDeviceManager device)
        {
            if (mouse.ScrollWheelValue != _previousMouse.ScrollWheelValue)
                _selectBloc.Update((mouse.ScrollWheelValue < _previousMouse.ScrollWheelValue) ? -1 : 1);

            #region KeyBoard
            bool[] dir = new bool[4];

            if (keyboard.IsKeyDown(_keys["Up"]))
                dir[0] = true;

            if (keyboard.IsKeyDown(_keys["Down"]))
                dir[1] = true;

            if (keyboard.IsKeyDown(_keys["Left"]))
                dir[2] = true;

            if (keyboard.IsKeyDown(_keys["Right"]))
                dir[3] = true;

            if (keyboard.IsKeyDown(_keys["Speed"]))
                Speed(true, gameTime.ElapsedGameTime.Milliseconds);
            else
                Speed(false, gameTime.ElapsedGameTime.Milliseconds);

            if (keyboard.IsKeyDown(_keys["Save"]) && _previousKeyboard.IsKeyUp(_keys["Save"]))
                Save(device.GraphicsDevice);

            if (keyboard.IsKeyDown(_keys["Load"]) && _previousKeyboard.IsKeyUp(_keys["Load"]))
                Load(device.GraphicsDevice);

            if (keyboard.IsKeyDown(_keys["FullScreen"]) && _previousKeyboard.IsKeyUp(_keys["FullScreen"]))
                FullScreen(device);

            if (dir[0])
                _top -= _speed;
            if (dir[1])
                _top += _speed;
            if (dir[2])
                _left -= _speed;
            if (dir[3])
                _left += _speed;

            _previousKeyboard = keyboard;
            #endregion

            #region Pointeur
            _pointeurPoint.X = mouse.X;
            _pointeurPoint.Y = mouse.Y;
            #endregion

            #region Mouse
            _selectedBloc.X=(int)((_pointeurPoint.X) / (float)_blocSize + _left);
            _selectedBloc.Y = (int)((_pointeurPoint.Y) / (float)_blocSize + _top);

            if (Map.Exist(_selectedBloc))
            {
                Block b = _map[_selectedBloc];
                if (mouse.RightButton == ButtonState.Pressed && _previousMouse.RightButton == ButtonState.Released)
                {
                    if (b.Bloc.ActionBloc != null)
                        b.Bloc = b.Bloc.ActionBloc.Invoke(b,
                            new Vector2((float)(_pointeurPoint.X + (_left - _selectedBloc.X) * _blocSize) / _blocSize,
                                (float)(_pointeurPoint.Y + (_top - _selectedBloc.Y) * _blocSize) / _blocSize),
                            gameTime.TotalGameTime.TotalMilliseconds);
                }
                else if (mouse.RightButton == ButtonState.Pressed)
                    PlaceBlock(gameTime.TotalGameTime.TotalMilliseconds);
                else if (mouse.LeftButton == ButtonState.Pressed)
                    PickBlock();
            }

            Update(gameTime, device);

            _previousMouse = mouse;
            #endregion
        }

        public void UpdateController(MouseState mouse, KeyboardState keyboard, GameTime gameTime, GraphicsDeviceManager device)
        {
            if (!GamePad.GetState(PlayerIndex.One).IsConnected)
            {
                Game1._mineCraft2DUpload = UpdateKeyboard;
                Alert.Alerte("GamePad deconnecté", Color.Red);
                return;
            }

            GamePadState gamepadState = GamePad.GetState(PlayerIndex.One);

            if (gamepadState.Buttons.LeftShoulder == ButtonState.Pressed && _previousPad.Buttons.LeftShoulder == ButtonState.Released)
                _selectBloc.Update(-1);
            else if (gamepadState.Buttons.RightShoulder == ButtonState.Pressed && _previousPad.Buttons.RightShoulder == ButtonState.Released)
                _selectBloc.Update(1);

            #region GamePad
            bool[] dir = new bool[4];

            int j = 0;
            foreach (var button in _xbox.Values)
            {
                if (gamepadState.IsButtonDown(button))
                {
                    dir[j] = true;
                    j = 0;
                    //break;
                }
                j++;
                if (j == 4)
                    break;
            }
            if (gamepadState.IsButtonDown(_xbox["Speed"]))
                Speed(true, gameTime.ElapsedGameTime.Milliseconds);
            else
                Speed(false, gameTime.ElapsedGameTime.Milliseconds);

            if (gamepadState.IsButtonDown(_xbox["Save"]))
            {
                Save(device.GraphicsDevice);
            }

            if (gamepadState.IsButtonDown(_xbox["Load"]))
            {
                Load(device.GraphicsDevice);
            }

            if (gamepadState.IsButtonDown(_xbox["FullScreen"]))
            {
                FullScreen(device);
            }

            if (gamepadState.ThumbSticks.Left.Y == 0)
            {
                if (dir[0])
                    _top -= _speed;
                if (dir[1])
                    _top += _speed;
            }
            else
                _top -= gamepadState.ThumbSticks.Left.Y * _speed;

            if (gamepadState.ThumbSticks.Left.X == 0)
            {
                if (dir[2])
                    _left -= _speed;
                if (dir[3])
                    _left += _speed;
            }
            else
                _left += gamepadState.ThumbSticks.Left.X * _speed;

            _previousPad = gamepadState;
            #endregion

            #region Pointeur
            _pointeurPoint.X += (int)(gamepadState.ThumbSticks.Right.X * 3);
            _pointeurPoint.Y -= (int)(gamepadState.ThumbSticks.Right.Y * 3);
            #endregion

            #region Mouse
            _selectedBloc.X = (int)((_pointeurPoint.X) / (float)_blocSize + _left);
            _selectedBloc.Y = (int)((_pointeurPoint.Y) / (float)_blocSize + _top);
            if (_selectedBloc.X >= 0 && _selectedBloc.Y >= 0 && _selectedBloc.X < Map.Length && _selectedBloc.Y < Map.Height)
                if (gamepadState.Triggers.Right == 1f)
                    PlaceBlock(gameTime.TotalGameTime.TotalMilliseconds);
                else if (gamepadState.Triggers.Left == 1f)
                    PickBlock();

            Update(gameTime, device);

            _previousMouse = mouse;
            #endregion
        }

        public void Draw(SpriteBatch spritebatch)
        {
            int xd = (int)_left;
            int yd = (int)_top;
            int startX = Math.Max(0, xd);
            int endX = Math.Min(_width + xd + 4, Map.Length);
            int startY=Math.Max(0, yd);
            int endY = Math.Min(_height + yd + 4, Map.Height);
            //Random random = new Random();

            for (int x = startX; x < endX; x++)
            {
                for (int y =startY; y < endY; y++)
                {
                    Block b = _map[new Point(x, y)];
                    Color light = new Color(0, 0, 0, 255 - (int)(255 * (float)(b.Light / 100f)));
                    Color select = new Color(0.1f, 0.1f, 0.1f, 0.45f);

                    //Bloc
                    spritebatch.Draw(b.Bloc.Image,
                        new Rectangle((int)((x - _left) * _blocSize), (int)((y - _top) * _blocSize), _blocSize, _blocSize),
                        Color.White);

                    //Bloc Selectionné
                    if (_selectedBloc.Equals(b.Position))
                        spritebatch.Draw(_selectBloc.SelectedBloc.Image,
                            new Rectangle((int)((x - _left) * _blocSize), (int)((y - _top) * _blocSize), _blocSize, _blocSize),
                            select);
                    //LifeBar
                    if (b.Bloc.Life < b.Bloc.MaxLife)
                    {
                        spritebatch.Draw(_pixel,
                            new Rectangle((int)((x - _left) * _blocSize), (int)(((y - _top) + 1) * _blocSize) - 3, _blocSize, 3),
                            Color.Blue);

                        spritebatch.Draw(_pixel,
                            new Rectangle((int)((x - _left) * _blocSize) + 1, (int)(((y - _top)+1) * _blocSize) - 2, (int)(_blocSize * ((float)b.Bloc.Life / b.Bloc.MaxLife)), 1),
                            Color.Green);
                    }

                    //Lumiere
                    spritebatch.Draw(_pixel,
                        new Rectangle((int)((x - _left) * _blocSize), (int)((y - _top) * _blocSize), _blocSize, _blocSize),
                        light);
                }
            }
            spritebatch.Draw(BlocSet.GetSingleton()[TypeBloc.Plank].Image, new Rectangle(0, 0, spritebatch.GraphicsDevice.DisplayMode.Width, 35), Color.White);
            _selectBloc.Draw(spritebatch);
            //Pointeur
            spritebatch.Draw(_pointeur,
                new Rectangle(_pointeurPoint.X - 3, _pointeurPoint.Y - 3, 7, 7),
                Color.White);
        }
        #endregion
    }
}
